package cn.com.surker.handler;

import cn.com.surker.base.BaseEntity;
import cn.com.surker.common.Code;
import cn.com.surker.control.dao.ControlExpendBatchDao;
import cn.com.surker.control.dao.ControlExpendDao;
import cn.com.surker.control.entity.ControlExpend;
import cn.com.surker.control.entity.ControlExpendBatch;
import cn.com.surker.control.entity.ControlInvoice;
import cn.com.surker.control.service.ErpControlInvoiceService;
import cn.com.surker.enums.StateEnum;
import cn.com.surker.enums.production.invoice.InvoiceProcessEnum;
import cn.com.surker.lab.dao.LabExpendBatchDao;
import cn.com.surker.lab.dao.LabExpendDao;
import cn.com.surker.lab.entity.LabExpend;
import cn.com.surker.lab.entity.LabExpendBatch;
import cn.com.surker.lab.service.ErpLabExpendService;
import cn.com.surker.production.entity.ProductionIndentDispatch;
import cn.com.surker.production.entity.ProductionInvoice;
import cn.com.surker.production.service.ErpCarService;
import cn.com.surker.production.service.ErpIndentDispatchService;
import cn.com.surker.production.service.ErpInvoiceService;
import cn.com.surker.redis.RedisUtils;
import cn.com.surker.spring.RestResultDto;
import cn.com.surker.thread.ThreadPools;
import cn.com.surker.util.DateUtil;
import cn.com.surker.util.StringUtils;
import cn.com.surker.workcon.common.ProDelEnum;
import cn.com.surker.workcon.service.ProdDelService;
import cn.com.surker.workcon.service.ProduceService;
import cn.com.surker.workcon.service.ProductService;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.transaction.RollbackException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * 进行对接工控机数据,
 * 为避免重复操作等,此类引入了redis锁操作,
 * 如需处理工控机数据请以此类为入口
 * 此类包含线程的启用关闭操作
 *
 * @author luotao
 * @date 2024/9/27
 */
@Slf4j
@Component
public class BCSStarter {

	@Resource
	private RedisUtils redisUtils;
	@Resource
	protected ProduceService bcsProduceService;
	@Resource
	private ProductService productService;
	@Autowired
	private BCSCoordinator bcsCoordinator;
	@Resource
	protected ProdDelService prodDelService;

	@Autowired
	private ErpIndentDispatchService erpDispatchService;
	@Autowired
	private ErpInvoiceService invoiceService;

	@Autowired
	private ErpCarService carService;

	@Resource
	private LabExpendDao labExpendDao;

	@Resource
	private ControlExpendDao controlExpendDao;

	@Resource
	private ControlExpendBatchDao controlExpendBatchDao;

	@Resource
	private LabExpendBatchDao labExpendBatchDao;
	@Autowired
	private ErpControlInvoiceService erpControlInvoiceService;

	//	中青林循环次数
	private static final int BCS_WHILE_COUNT = 15;

	public static final Map<Long, Boolean> proDispatchCache = new ConcurrentHashMap<>();


	/**
	 * 用于查询工控机的key
	 */
	//	步骤1,查询派单是否接收
	public static final String REDIS_BCS_DISPATCH_KEY = "REDIS_BCS_DISPATCH_KEY";
	//	步骤2.1,派单接收后,查询工控的正在生产的数据
	public static final String REDIS_BCS_DIS_PRODING_KEY = "REDIS_BCS_DIS_PRODING_KEY";
	//	步骤2.2,派单接收后,查询工控的派单数据
	public static final String REDIS_BCS_DIS_INVOICE_KEY = "REDIS_BCS_DIS_INVOICE_KEY";
	//	步骤3,工控有发货单派单数据后,查询消耗
	public static final String REDIS_BCS_DIS_MATERIAL_KEY = "REDIS_BCS_DIS_MATERIAL_KEY";
	//	删除数据是否接收
	public static final String REDIS_BCS_PRO_DEL_ERP_KEY = "REDIS_BCS_PRO_DEL_ERP_KEY";
	public static final String REDIS_BCS_PRO_DEL_BCS_KEY = "REDIS_BCS_PRO_DEL_BCS_KEY";
	// 调度消息
	public static final String REDIS_BCS_DISPATCH_ERROR_MSG_KEY = "REDIS_BCS_DISPATCH_ERROR_MSG_KEY";
	//	同步工控机的手动消耗
	private static final String REDIS_BCS_SYNC_CONTROL_MANUAL_MATERIAL_KEY = "REDIS_BCS_SYNC_CONTROL_MANUAL_MATERIAL_KEY";
	//	重新匹配没有仓位的发货单
	private static final String REDIS_BCS_SYNC_INVOICE_NO_STORAGE_KEY = "REDIS_BCS_SYNC_INVOICE_NO_STORAGE_KEY";
	//	重新同步全部的发货单消耗,包括盘和消耗
	private static final String REDIS_BCS_RESET_INVOICE_LAB_EXPEND_KEY = "REDIS_BCS_SYNC_INVOICE_ALL_LAB_EXPEND_KEY";

	/**
	 * 开始处理派单信息,
	 * 所有的对派单处理的逻辑都在此处进行处理,地方如需要处理,调用此接口
	 *
	 * @param dispatchId 派单id
	 */
	@Transactional(rollbackFor = RollbackException.class, propagation = Propagation.REQUIRES_NEW)
	public synchronized void startDispatch(Long dispatchId) {
		try {
			//	线程等待500毫秒后,在执行,为避免在事务提交之前,就进行查询数据
			Thread.sleep(500);
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (dispatchId == null) {
			return;
		}
		//	用于redis加锁的key
		String redisLockKey = REDIS_BCS_DISPATCH_KEY + "_" + dispatchId;
		boolean acquire = redisUtils.acquireLock(redisLockKey);
		if (!acquire) {
			//	没有加锁成功,不执行
			log.info("派单没有加锁成功:" + dispatchId);
			return;
		}
		//	启动定时任务线程,查询该派单是否接收
		//	获取线程池
		ScheduledThreadPoolExecutor scheduler = ThreadPools.getScheduledThreadPool();
		//	记录执行次数
		AtomicInteger count = new AtomicInteger(0);
		//	设置返回值
		AtomicReference<ScheduledFuture<?>> scheduledFutureRef = new AtomicReference<>();
		//	执行线程
		scheduledFutureRef.set(scheduler.scheduleAtFixedRate(() -> {
			int i = count.get();
			//	根据erp的派单信息,查询工控机是否进行接收派单
			//	此处要加锁,此处用redis的分布式琐,因为统一了入口,所以不会有其他地方操作
			ProductionIndentDispatch erpDispatch = erpDispatchService.getById(dispatchId);
			if (!BaseEntity.validateExist(erpDispatch)) {
				//	派单任务不存在,关闭线程
				log.info("派单任务不存在:" + dispatchId);
				//	清除缓存
				redisUtils.removeList(REDIS_BCS_DISPATCH_KEY, 0, String.valueOf(dispatchId));
				//	释放琐
				redisUtils.releaseLock(redisLockKey);
				//	关闭线程
				scheduledFutureRef.get().cancel(false);
				return;
			}
			Long lineId = erpDispatch.getLineId();
			//	生产状态
			Integer proStatus = erpDispatch.getProStatus();
			if (!InvoiceProcessEnum.QUERN_RECEIVE.getCode().equals(proStatus)) {
				log.info("派单数据不是等待过程:" + JSONObject.toJSONString(erpDispatch));
				//	不是等待中的状态,不进行操作,清楚缓存,关闭线程
				redisUtils.removeList(REDIS_BCS_DISPATCH_KEY, 0, String.valueOf(dispatchId));
				//	释放琐
				redisUtils.releaseLock(redisLockKey);
				//	关闭线程
				scheduledFutureRef.get().cancel(false);
				return;
			}
			Integer wcProStatus = null;
			try {
				//	查询工控机的派车信息
				wcProStatus = bcsProduceService.queryByDispatchId(lineId, dispatchId);
			} catch (Exception e) {
				e.printStackTrace();
			}
			if (wcProStatus == null) {
				//	没有查询出数据,修改派单状态为接收失败,清楚缓存关闭线程
				dispatchFail(erpDispatch, "工控机未查询出派单,因此取消", scheduledFutureRef);
				//	释放琐
				redisUtils.releaseLock(redisLockKey);
				//	关闭线程
				scheduledFutureRef.get().cancel(false);
				return;
			}
			if (InvoiceProcessEnum.QUERN_PRO.getCode().equals(wcProStatus) || InvoiceProcessEnum.PRO.getCode().equals(wcProStatus)) {
				//	成功接收,修改派单状态,删除redis缓存,关闭线程
				erpDispatch.setProStatus(InvoiceProcessEnum.QUERN_PRO.getCode());
				erpDispatchService.updateById(erpDispatch);
				ProductionInvoice invoice = invoiceService.getById(erpDispatch.getInvoiceId());
				if (BaseEntity.validateExist(invoice)) {
					//	更新发货单状态
					invoice.setProcess(InvoiceProcessEnum.QUERN_PRO.getCode());
					invoiceService.updateById(invoice);
				}
				//	开始查询发货单信息
				redisUtils.rightPush(REDIS_BCS_DIS_INVOICE_KEY, String.valueOf(dispatchId));
				//	查询正在生产的信息
				ThreadPoolExecutor commonThreadPool = ThreadPools.getCommonThreadPool();
				commonThreadPool.execute(new Runnable() {
					@Override
					public void run() {
						//	确保是新事务
						startProIng(dispatchId);
					}
				});
				//	清除缓存
				redisUtils.removeList(REDIS_BCS_DISPATCH_KEY, 0, String.valueOf(dispatchId));
				//	释放琐
				redisUtils.releaseLock(redisLockKey);
				//	关闭线程
				scheduledFutureRef.get().cancel(false);
			}
			if (i > BCS_WHILE_COUNT) {
				// 表示派单失败,修改派单状态,删除redis缓存,关闭线程
				//	删除派单数据
				Integer delNum = null;
				try {
					delNum = bcsProduceService.delDispatch(lineId, dispatchId);
					//	提交事务
					bcsProduceService.commitTranLine(lineId);
				} catch (Exception e) {
					e.printStackTrace();
					//	回归事务
					bcsProduceService.rollbackLine(lineId);
				}
				if (delNum > 0) {
					//	成功删除
					dispatchFail(erpDispatch, "工控机接收派单超时,因此取消", scheduledFutureRef);
					//	存缓存信息
					Long orgId = erpDispatch.getOrgId();
					Long carId = erpDispatch.getCarId();
					String carCode = carService.queryCarCode(orgId, carId, DateUtil.convertString(new Date()));
					String disMsg = "车号[" + carCode + "]派单失败:工控机接收派单超时,因此取消;";
					redisUtils.leftPush(REDIS_BCS_DISPATCH_ERROR_MSG_KEY, disMsg);
					//	释放琐
					redisUtils.releaseLock(redisLockKey);
					//	关闭线程
					scheduledFutureRef.get().cancel(false);
				} else {
					//	删除失败,查询是否正在删除,重新执行一次线程即可
				}
			}
			// 执行一次加1
			count.incrementAndGet();
		}, 0, 2, TimeUnit.SECONDS));

	}

	/**
	 * 查询发货单是否正在生产
	 *
	 * @param dispatchId
	 */
	@Transactional(rollbackFor = RollbackException.class, propagation = Propagation.REQUIRED)
	public void startProIng(Long dispatchId) {
		if (dispatchId == null) {
			return;
		}
		//	用于redis加锁的key
		String redisLockKey = REDIS_BCS_DIS_PRODING_KEY + "_" + dispatchId;
		boolean acquire = redisUtils.acquireLock(redisLockKey);
		if (!acquire) {
			//	没有加锁成功,不执行
			return;
		}
		//	查询派单信息
		ProductionIndentDispatch dispatch = erpDispatchService.getById(dispatchId);
		if (!BaseEntity.validateExist(dispatch)) {
			//	信息不存在
			return;
		}
		Long lineId = dispatch.getLineId();
		Integer proStatus = dispatch.getProStatus();
		if (!InvoiceProcessEnum.QUERN_PRO.getCode().equals(proStatus)) {
			//	派单不是等待生产的过程
			return;
		}
		//	启动定时任务查询
		//	获取线程池
		ScheduledThreadPoolExecutor scheduler = ThreadPools.getScheduledThreadPool();
		//	设置返回值
		AtomicReference<ScheduledFuture<?>> scheduledFutureRef = new AtomicReference<>();
		//	执行线程
		proDispatchCache.put(dispatchId, true);
		scheduledFutureRef.set(scheduler.scheduleAtFixedRate(() -> {
			Boolean tempProstatus = proDispatchCache.get(dispatchId);
			if (tempProstatus != null && tempProstatus) {
				//	正常操作,查询派单信息
				Boolean inProing = null;
				try {
					inProing = productService.inProing(lineId, dispatchId);
				} catch (Exception e) {
					e.printStackTrace();
				}
				if (inProing) {
					//	有数据就表示正在生产,修改状态
					dispatch.setProStatus(InvoiceProcessEnum.PRO.getCode());
					erpDispatchService.updateById(dispatch);
					ProductionInvoice invoice = invoiceService.getById(dispatch.getInvoiceId());
					if (BaseEntity.validateExist(invoice)) {
						invoice.setProcess(InvoiceProcessEnum.PRO.getCode());
						invoiceService.updateById(invoice);
					}
					//	开启新线程,查询发货单信息
					ThreadPoolExecutor commonThreadPool = ThreadPools.getCommonThreadPool();
					commonThreadPool.execute(new Runnable() {
						@Override
						public void run() {
							//	确保是新事务
							startInvoice(dispatchId);
						}
					});
					//	清除缓存
					redisUtils.removeList(REDIS_BCS_DIS_PRODING_KEY, 0, String.valueOf(dispatchId));
					//	释放琐
					redisUtils.releaseLock(redisLockKey);
					//	关闭线程
					scheduledFutureRef.get().cancel(false);
				} else {
					//	查询派单是否被删除
					Boolean disHasDel = bcsProduceService.disHasDel(lineId, dispatchId);
					if (disHasDel != null && disHasDel) {
						//	工控机删除了数据,删除派单信息
						String remark = "工控机删除了数据,程序同步删除";
						dispatch.setProStatus(InvoiceProcessEnum.DEL.getCode());
						dispatch.setStatus(StateEnum.DISABLED.getCode());
						dispatch.addRemark(remark);
						erpDispatchService.updateById(dispatch);
						//	删除派单后,查询该发货单是否存在其他派单
						Long invoiceId = dispatch.getInvoiceId();
						//	取消发货单
						bcsCoordinator.cancelInvoice(invoiceId, remark);
						//	释放锁
						redisUtils.releaseLock(redisLockKey);
						//	清除派单缓存
						redisUtils.removeList(REDIS_BCS_DIS_INVOICE_KEY, 0, String.valueOf(dispatchId));
						//	关闭线程
						scheduledFutureRef.get().cancel(false);
					}
				}
			} else {
				//	截止循环
				//	释放锁
				redisUtils.releaseLock(redisLockKey);
				//	清除派单缓存
				redisUtils.removeList(REDIS_BCS_DIS_INVOICE_KEY, 0, String.valueOf(dispatchId));
				//	关闭线程
				scheduledFutureRef.get().cancel(false);
			}
		}, 0, 2, TimeUnit.SECONDS));
	}


	/**
	 * 查询发货单信息;一个发货单对应多个派单,此处仅根据发货单的派单进行查询,对发货单数据进行补充
	 *
	 * @param dispatchId 派单id
	 */
	@Transactional(rollbackFor = RollbackException.class, propagation = Propagation.REQUIRED)
	public void startInvoice(Long dispatchId) {
		if (dispatchId == null) {
			return;
		}
		ProductionIndentDispatch dispatch = erpDispatchService.getById(dispatchId);
		if (!BaseEntity.validateExist(dispatch)) {
			return;
		}
		//	判断是否已经处理
		Integer disStatus = dispatch.getProStatus();
		if (!InvoiceProcessEnum.QUERN_RECEIVE.getCode().equals(disStatus)
			&& !InvoiceProcessEnum.QUERN_PRO.getCode().equals(disStatus)
			&& !InvoiceProcessEnum.PRO.getCode().equals(disStatus)) {
			//	不为待接收状态
			return;
		}
		//	用于redis加锁的key
		String redisLockKey = REDIS_BCS_DIS_INVOICE_KEY + "_" + dispatchId;
		boolean acquire = redisUtils.acquireLock(redisLockKey);
		if (!acquire) {
			//	没有加锁成功,不执行
			return;
		}
		//	启动定时任务查询发货单信息
		//	获取线程池
		ScheduledThreadPoolExecutor scheduler = ThreadPools.getScheduledThreadPool();
		//	设置返回值
		AtomicReference<ScheduledFuture<?>> scheduledFutureRef = new AtomicReference<>();
		//	执行线程
		proDispatchCache.put(dispatchId, true);
		scheduledFutureRef.set(scheduler.scheduleAtFixedRate(() -> {
			//	查询派单信息
			//	取出缓存,判断是否中止了线程
			Boolean tempProstatus = proDispatchCache.get(dispatchId);
			if (tempProstatus != null && tempProstatus) {
				//	正常查询操作
				Boolean b = bcsCoordinator.convertInvoice(dispatchId);
				if (b != null && b) {
					//	开始打印
					/*ThreadPoolExecutor commonThreadPool = ThreadPools.getCommonThreadPool();
					commonThreadPool.execute(new Runnable() {
						@Override
						public void run() {
							printInvoice(dispatch.getInvoiceId());
						}
					});*/
					proDispatchCache.remove(dispatchId);
					//	查询发货单成功,进行查询发货单下的盘,以及消耗信息
					redisUtils.rightPush(REDIS_BCS_DIS_MATERIAL_KEY, String.valueOf(dispatchId));
					startMaterial(dispatchId);
					//	清除缓存
					redisUtils.removeList(REDIS_BCS_DIS_INVOICE_KEY, 0, String.valueOf(dispatchId));
					//	释放琐
					redisUtils.releaseLock(redisLockKey);
					//	关闭线程
					scheduledFutureRef.get().cancel(false);
				}
			} else {
				//	截止循环,
				//	清除缓存
				redisUtils.removeList(REDIS_BCS_DIS_INVOICE_KEY, 0, String.valueOf(dispatchId));
				//	释放琐
				redisUtils.releaseLock(redisLockKey);
				//	关闭线程
				scheduledFutureRef.get().cancel(false);
			}
		}, 1, 5, TimeUnit.SECONDS));

	}


	/**
	 * 查询消耗
	 * @param dispatchId
	 */
	public void startMaterial(Long dispatchId) {
		if (dispatchId == null) {
			return;
		}
		//	用于redis加锁的key
		String redisLockKey = REDIS_BCS_DIS_MATERIAL_KEY + "_" + dispatchId;
		boolean acquire = redisUtils.acquireLock(redisLockKey);
		if (!acquire) {
			//	没有加锁成功,不执行
			return;
		}
		//	获取线程池
		ScheduledThreadPoolExecutor scheduler = ThreadPools.getScheduledThreadPool();
		//	设置返回值
		AtomicReference<ScheduledFuture<?>> scheduledFutureRef = new AtomicReference<>();
		//	执行线程
		scheduledFutureRef.set(scheduler.scheduleAtFixedRate(() -> {
			//	查询派单信息
			Boolean aBoolean = null;
			try {
				aBoolean = bcsCoordinator.convertMaterial(dispatchId, null);
			} catch (Exception e) {
				e.printStackTrace();
			}
			if (aBoolean != null && aBoolean) {
				//	清除缓存
				redisUtils.removeList(REDIS_BCS_DIS_MATERIAL_KEY, 0, String.valueOf(dispatchId));
				//	释放琐
				redisUtils.releaseLock(redisLockKey);
				//	关闭线程
				scheduledFutureRef.get().cancel(false);
			}
		}, 60, 5, TimeUnit.SECONDS));
	}

	/**
	 * 同步工控机的发货单
	 */
	public void syncControlInvoice() {
		//	用于redis加锁的key
		String redisLockKey = "REDIS_BCS_SYNC_CONTROL_INVOICE_KEY";
		boolean acquire = redisUtils.acquireLock(redisLockKey);
		if (!acquire) {
			//	没有加锁成功,不执行
			return;
		}
		List<ControlInvoice> controlInvoices = bcsCoordinator.syncControlInvoice();
		//	释放琐
		redisUtils.releaseLock(redisLockKey);
	}

	/**
	 * 开始同步工控机的手动消耗
	 *
	 */
	public void convertManuale() {
		String redisLockKey = REDIS_BCS_SYNC_CONTROL_MANUAL_MATERIAL_KEY;
		boolean acquire = redisUtils.acquireLock(redisLockKey);
		if (!acquire) {
			//	没有加锁成功,不执行
			return;
		}
		bcsCoordinator.convertManuale();
		//	释放琐
		redisUtils.releaseLock(redisLockKey);
	}

	public void createHandInv() {
		String redisLockKey = "redis_create_manual_pro_stere_key";
		boolean acquire = redisUtils.acquireLock(redisLockKey);
		if (!acquire) {
			//	没有加锁成功,不执行
			return;
		}
		bcsCoordinator.createHandInv();
		//	释放琐
		redisUtils.releaseLock(redisLockKey);
	}

	/**
	 * 重新匹配发货单缺失的消耗仓位
	 *
	 * @param invoiceId 发货单id
	 * @param startTime 起始时间
	 * @param endTime
	 */
	public void syncInvoiceStorage(Long invoiceId, String startTime, String endTime) {
		String redisLockKey = REDIS_BCS_SYNC_INVOICE_NO_STORAGE_KEY + "_" + invoiceId;
		boolean acquire = redisUtils.acquireLock(redisLockKey);
		if (!acquire) {
			//	没有加锁成功,不执行
			return;
		}
		bcsCoordinator.syncInvoiceStorage(invoiceId, startTime, endTime);
		//	释放琐
		redisUtils.releaseLock(redisLockKey);
	}


	/**
	 * 重新匹配发货单缺失的消耗仓位
	 *
	 * @param invoiceId 发货单id
	 */
	public void resetInvoiceExpend(Long invoiceId) {
		String redisLockKey = REDIS_BCS_RESET_INVOICE_LAB_EXPEND_KEY + "_" + invoiceId;
		boolean acquire = redisUtils.acquireLock(redisLockKey);
		if (!acquire) {
			//	没有加锁成功,不执行
			return;
		}
		ProductionInvoice oldInvoice = invoiceService.getById(invoiceId);
		if (oldInvoice == null) {
			return;
		}
		//	手动关联的不操作
		Long controlInvoiceId = oldInvoice.getControlInvoiceId();
		if (controlInvoiceId != null) {
			//	根据手动发货单关联消耗
			bcsCoordinator.delInvoiceExpend(invoiceId);
			ControlInvoice controlInvoice = erpControlInvoiceService.getById(controlInvoiceId);
			linkConInv2Erp(invoiceId, controlInvoice);
			return;
		}
		//	清除发货单数据
		bcsCoordinator.delInvoiceExpend(invoiceId);
		//	发货单数据
		ProductionInvoice invoice = null;
		//	发货单数据是否需要更新
		boolean updateInv = false;
		//	查询发货单下的派单数据
		List<ProductionIndentDispatch> dispatches = erpDispatchService.queryByInvoice(invoiceId);
		if (dispatches != null && dispatches.size() > 0) {
			for (ProductionIndentDispatch dispatch : dispatches) {
				Long dispatchId = dispatch.getId();
				Long lineId = dispatch.getLineId();
				ProductionIndentDispatch conProduct = productService.queryByDispatchId(lineId, dispatchId);
				if (conProduct != null) {
					//	生产方量
					Double conProStere = conProduct.getProStere();
					//	生产时间
					Date proTime = conProduct.getProTime();
					//	工控机发货单id
					String prodId = conProduct.getControlId();
					//	查询是否有生产方量
					Double proStere = dispatch.getProStere();
					if (proStere == null || proStere.equals(0d)) {
						//	没有生产方量,重新查询生产单信息
						dispatch.setProStere(conProStere);
						dispatch.setProTime(proTime);
						dispatch.setProStatus(InvoiceProcessEnum.PRO_COMPLETE.getCode());
						dispatch.setControlId(prodId);
						//	更新数据
						erpDispatchService.updateById(dispatch);
					}
					//	毕竟发货单的生产完成时间
					if (proTime != null) {
						if (invoice == null) {
							invoice = invoiceService.getById(invoiceId);
						}
						Date invoiceProTime = invoice.getProTime();
						if (invoiceProTime == null) {
							//	设置时间
							invoice.setProTime(proTime);
							updateInv = true;
						} else {
							//	判断时间大小
							if (invoiceProTime.getTime() < proTime.getTime()) {
								//	发货单的时间小于了工控机的生产时间
								invoice.setProTime(proTime);
								updateInv = true;
							}
						}
					}
				}
				startMaterial(dispatchId);
			}
		}
		if (updateInv) {
			invoiceService.updateById(invoice);
		}
		//	释放琐
		redisUtils.releaseLock(redisLockKey);
	}

	/**
	 * 开始处理删除派单信息
	 *
	 * @param dispatchId
	 */
	public RestResultDto startDelDis(Long dispatchId) {
		if (dispatchId == null) {
			return RestResultDto.fail(Code.PARAM_EMPTY);
		}
		//	查询调度数据
		ProductionIndentDispatch erpDisPatch = erpDispatchService.getById(dispatchId);
		if (!BaseEntity.validateExist(erpDisPatch)) {
			return RestResultDto.fail(Code.NO_DATA);
		}
		//	生产线
		Long lineId = erpDisPatch.getLineId();
		//	生产状态
		Integer proStatus = erpDisPatch.getProStatus();
		if (!InvoiceProcessEnum.QUERN_PRO.getCode().equals(proStatus) && !InvoiceProcessEnum.QUERN_FAIL.getCode().equals(proStatus)
			&& !InvoiceProcessEnum.QUERN_RECEIVE.getCode().equals(proStatus)) {
			//	只有等待中的派单才允许删除
			return RestResultDto.fail("只有未生产的派单才允许删除");
		}
		/*if (proStatus != null && proStatus > 1) {
			//	只有等待中的派单才允许删除
			return RestResultDto.fail("只有未生产的派单才允许删除");
		}*/
		//	查询是否正在生产
		Boolean inProing = null;
		try {
			inProing = productService.inProing(lineId, dispatchId);
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (inProing) {
			//	存在正在生产的数据
			return RestResultDto.fail("已经开始生产,无法取消");
		}
		Integer wcProStatus = null;
		try {
			//	查询工控机的派车信息
			wcProStatus = bcsProduceService.queryByDispatchId(lineId, dispatchId);
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (wcProStatus != null) {
			if (InvoiceProcessEnum.QUERN_PRO.getCode().equals(wcProStatus)) {
				//	成功接收
				//startDispatch(dispatchId);
				//	TODO 工控接收后,不能删
				//return RestResultDto.fail("已经生产完成,无法取消,正在同步消耗");
			}
		}
		//	用于redis加锁的key
		String redisLockKey = REDIS_BCS_PRO_DEL_ERP_KEY + "_" + dispatchId;
		boolean acquire = redisUtils.acquireLock(redisLockKey);
		if (!acquire) {
			//	没有加锁成功,不执行
			return RestResultDto.fail("已经在删除中,请等待");
		}
		//	保存数据
		try {
			prodDelService.insert(lineId, dispatchId, erpDisPatch.getCode());
			//	修改派单数据为等待删除
		} catch (Exception e) {
			e.printStackTrace();
			prodDelService.rollbackLine(lineId);
			return RestResultDto.fail("对工控发送删除数据指令失败");
		}
		prodDelService.commitTranLine(lineId);
		//	删除琐
		redisUtils.releaseLock(redisLockKey);
		//	启用线程,查询该删除派单是否成功执行
		ThreadPoolExecutor commonThreadPool = ThreadPools.getCommonThreadPool();
		commonThreadPool.execute(new Runnable() {
			@Override
			public void run() {
				//	确保是新事务
				queryDelDisRes(lineId, dispatchId);
			}
		});
		return RestResultDto.success();
	}

	/**
	 * 发货单关联工控机erp
	 *
	 * @param invoiceId
	 * @param controlInvoice
	 */
	public void linkConInv2Erp(Long invoiceId, ControlInvoice controlInvoice) {
		String redisLockKey = "LINK_CON_INV_TO_ERP_" + invoiceId;
		boolean acquire = redisUtils.acquireLock(redisLockKey);
		if (!acquire) {
			//	没有加锁成功,不执行
			return;
		}
		bcsCoordinator.linkConInv2Erp(invoiceId, controlInvoice);
		//	释放琐
		redisUtils.releaseLock(redisLockKey);
	}


	/**
	 * 查询派单结果
	 * @param dispatchId            erp派单信息
	 */
	private synchronized void queryDelDisRes(Long lineId, Long dispatchId) {
		if (lineId == null || dispatchId == null) {
			return;
		}
		//	用于redis加锁的key
		String redisLockKey = REDIS_BCS_PRO_DEL_BCS_KEY + "_" + dispatchId;
		boolean acquire = redisUtils.acquireLock(redisLockKey);
		if (!acquire) {
			//	没有加锁成功,不执行
			return;
		}
		//	获取线程池
		ScheduledThreadPoolExecutor scheduler = ThreadPools.getScheduledThreadPool();
		//	记录执行次数
		AtomicInteger count = new AtomicInteger(0);
		//	设置返回值
		AtomicReference<ScheduledFuture<?>> scheduledFutureRef = new AtomicReference<>();
		//	执行线程
		scheduledFutureRef.set(scheduler.scheduleAtFixedRate(() -> {
			//	查询数据
			Integer flag = prodDelService.selectByDisId(lineId, dispatchId);
			if (flag == null) {
				//	没有查询出数据,清除缓存,关闭线程,释放琐
				//redisUtils.removeList(REDIS_BCS_PRO_DEL_BCS_KEY, 0, String.valueOf(dispatchId));
				scheduledFutureRef.get().cancel(false);
				redisUtils.releaseLock(redisLockKey);
				return;
			}
			//	查询erp的派单信息
			ProductionIndentDispatch erpDis = erpDispatchService.getById(dispatchId);
			if (erpDis == null) {
				//	没有查询出数据,清除缓存,关闭线程,释放琐
				//redisUtils.removeList(REDIS_BCS_PRO_DEL_BCS_KEY, 0, String.valueOf(dispatchId));
				redisUtils.releaseLock(redisLockKey);
				scheduledFutureRef.get().cancel(false);
				return;
			}
			if (ProDelEnum.SUCCESS.getCode().equals(flag)) {
				//	删除成功,修改派单为删除,如果发货单下仅只有这个派单也删除
				String remark = "用户取消了派单,工控机删除成功";
				erpDis.setProStatus(InvoiceProcessEnum.DEL.getCode());
				erpDis.setStatus(StateEnum.DISABLED.getCode());
				erpDis.addRemark(remark);
				erpDispatchService.updateById(erpDis);
				//	删除派单后,查询该发货单是否存在其他派单
				Long invoiceId = erpDis.getInvoiceId();
				//	取消发货单
				bcsCoordinator.cancelInvoice(invoiceId, remark);
				//	清除缓存
				redisUtils.removeList(REDIS_BCS_DIS_INVOICE_KEY, 0, String.valueOf(dispatchId));
				//	释放锁
				redisUtils.releaseLock(redisLockKey);
				//	关闭线程
				scheduledFutureRef.get().cancel(false);
			} else if (ProDelEnum.FAIL.getCode().equals(flag)) {
				//	删除失败
				erpDis.addRemark("工控删除派单失败");
				Long invoiceId = erpDis.getInvoiceId();
				bcsCoordinator.cancelInvoice(invoiceId, "工控删除派单失败");
				erpDispatchService.updateById(erpDis);
				//	清除缓存,是否琐,关闭线程
				// redisUtils.removeList(REDIS_BCS_PRO_DEL_BCS_KEY, 0, String.valueOf(dispatchId));
				redisUtils.releaseLock(redisLockKey);
				scheduledFutureRef.get().cancel(false);
			} else {
				//	查询是否在生产
				String proRemark = "";
				Boolean hasPro = false;
				Boolean inProing = productService.inProing(lineId, dispatchId);
				if (inProing) {
					//	存在正在生产的数据,清除缓存,关闭线程,新增备注,删除erp指令数据
					proRemark = "用户取消了派单,但是工控机已经在生产,取消删除";
				} else {
					//	查询调度数据
					Integer wcProStatus = bcsProduceService.queryByDispatchId(lineId, dispatchId);
					log.info("用户取消派单,查询的派单状态:{},erp派单id:{}", wcProStatus, dispatchId);
					if (wcProStatus != null) {
						//	判断工控机调度数据的生产状态
						if (InvoiceProcessEnum.PRO.getCode().equals(wcProStatus)) {
							//	生产中
							hasPro = true;
							proRemark = "派单正在生产";
						} else if (InvoiceProcessEnum.PRO_COMPLETE.getCode().equals(wcProStatus)) {
							//	生产完成
							hasPro = true;
							proRemark = "派单生产完成(含生产中断的情况)";
						}
					}
					if (!hasPro) {
						//	查询发货单数据
						ProductionIndentDispatch indentDispatch = productService.queryByDispatchId(lineId, dispatchId);
						if (indentDispatch != null) {
							hasPro = true;
							proRemark = "用户取消了派单,但是工控机已经生产生产完成,取消删除";
						}
					}
				}
				if (hasPro) {
					//	已经有生产了
					erpDis.addRemark(proRemark);
					erpDispatchService.updateById(erpDis);
					//	清除缓存
					// redisUtils.removeList(REDIS_BCS_PRO_DEL_BCS_KEY, 0, String.valueOf(dispatchId));
					//	释放锁
					redisUtils.releaseLock(redisLockKey);
					//	关闭线程
					scheduledFutureRef.get().cancel(false);
					//	删除数据
					try {
						prodDelService.deleteByDisId(lineId, dispatchId);
						prodDelService.commitTranLine(lineId);
					} catch (Exception e) {
						e.printStackTrace();
						prodDelService.rollbackLine(lineId);
					}
					return;
				}
				int i = count.get();
				if (i > BCS_WHILE_COUNT) {
					//	表示执行5次完了后,仍然没用处理结果,表示删除工控机不在线,删除工控机的派单记录
					//	删除数据
					try {
						//	删除派单数据
						bcsProduceService.delDispatch(lineId, dispatchId);
						// prodDelService.commitTranLine(lineId);
						String remark = "用户取消了派单,工控机删除超时,强制删除";
						erpDis.setProStatus(InvoiceProcessEnum.DEL.getCode());
						erpDis.setStatus(StateEnum.DISABLED.getCode());
						erpDis.addRemark(remark);
						erpDispatchService.updateById(erpDis);
						//	删除派单后,查询该发货单是否存在其他派单
						Long invoiceId = erpDis.getInvoiceId();
						//	删除发货单
						bcsCoordinator.cancelInvoice(invoiceId, remark);
						//	提交事务
						bcsProduceService.commitTranLine(lineId);
						//	清除缓存
						redisUtils.removeList(REDIS_BCS_DIS_INVOICE_KEY, 0, String.valueOf(dispatchId));
						//	释放锁
						redisUtils.releaseLock(redisLockKey);
						//	关闭线程
						scheduledFutureRef.get().cancel(false);
					} catch (Exception e) {
						e.printStackTrace();
						bcsProduceService.rollbackLine(lineId);
					}
				}
			}
			// 执行一次加1
			count.incrementAndGet();
		}, 0, 1, TimeUnit.SECONDS));
	}

	/**
	 * 处理派单失败
	 *
	 * @param erpDispatch
	 * @param scheduledFutureRef
	 */
	private void dispatchFail(ProductionIndentDispatch erpDispatch, String remark, AtomicReference<ScheduledFuture<?>> scheduledFutureRef) {
		erpDispatch.setProStatus(InvoiceProcessEnum.QUERN_FAIL.getCode());
		erpDispatch.addRemark(remark);
		erpDispatch.setStatus(StateEnum.DISABLED.getCode());
		erpDispatchService.updateById(erpDispatch);
		Long invoiceId = erpDispatch.getInvoiceId();
		bcsCoordinator.cancelInvoice(invoiceId, remark);
		redisUtils.removeList(REDIS_BCS_DISPATCH_KEY, 0, String.valueOf(erpDispatch.getId()));
		scheduledFutureRef.get().cancel(false);
	}

	/**
	 * 从备份库同步仓位
	 *
	 * @param invoiceId
	 * @param startTime
	 * @param endTime
	 */
	@Deprecated
	public void syncBackStorage(Long invoiceId, String startTime, String endTime) {
		String redisLockKey = REDIS_BCS_SYNC_INVOICE_NO_STORAGE_KEY + "_" + invoiceId;
		boolean acquire = redisUtils.acquireLock(redisLockKey);
		if (!acquire) {
			//	没有加锁成功,不执行
			return;
		}
		bcsCoordinator.syncBackStorage(invoiceId, startTime, endTime);
		//	释放琐
		redisUtils.releaseLock(redisLockKey);
	}

	public void syncAllControl(String startTime, String endTime) {
		//	用于redis加锁的key
		String redisLockKey = "REDIS_BCS_SYNC_CONTROL_ALL_KEY";
		boolean acquire = redisUtils.acquireLock(redisLockKey);
		if (!acquire) {
			//	没有加锁成功,不执行
			return;
		}
		bcsCoordinator.syncAllControl(startTime, endTime);
		//	释放琐
		redisUtils.releaseLock(redisLockKey);
	}

	/**
	 * 同步没有盘的数据
	 *
	 * @param startTime
	 * @param endTime
	 */
	public void syncNoBatchInv(String startTime, String endTime) {
		String redisLockKey = "REDIS_BCS_SYNC_CONTROL_TO_ERP";
		boolean acquire = redisUtils.acquireLock(redisLockKey);
		if (!acquire) {
			//	没有加锁成功,不执行
			return;
		}
		List<ProductionInvoice> invoiceList = invoiceService.findNoBatchInv(startTime, endTime);
		if (invoiceList != null || invoiceList.size() > 0) {
			for (int i = 0, s = invoiceList.size(); i < s; i++) {
				ProductionInvoice invoice = invoiceList.get(i);
				Long invoiceId = invoice.getId();
				resetInvoiceExpend(invoiceId);
			}
		}
		//	释放琐
		redisUtils.releaseLock(redisLockKey);
	}

	/**
	 * 全量同步工控数据到erp数据
	 *
	 * @param startTime
	 * @param endTime
	 */
	public void syncNoLab(String startTime, String endTime) {
		String redisLockKey = "REDIS_BCS_SYNC_CONTROL_TO_ERP";
		boolean acquire = redisUtils.acquireLock(redisLockKey);
		if (!acquire) {
			//	没有加锁成功,不执行
			return;
		}
		// controlExpendDao.findErpNolab(startTime,endTime);
		QueryWrapper<LabExpend> qwLe = new QueryWrapper<>();
		qwLe.eq("status", StateEnum.ENABLED.getCode());
		qwLe.inSql("invoice_id", "SELECT id from production_invoice WHERE pro_time >= '" + startTime + "' and pro_time < '" + endTime + "'");
		List<LabExpend> expendList = labExpendDao.selectList(qwLe);
		//	取出id
		List<String> labExpendWcIds = expendList.stream().filter(e -> {
			if (e == null) {
				return false;
			}
			String conExpendId = e.getConExpendId();
			if (StringUtils.isNotBlank(conExpendId)) {
				return true;
			} else {
				return false;
			}
		}).map(LabExpend::getConExpendId).collect(Collectors.toList());
		//	查询工控消耗数据
		QueryWrapper<ControlExpend> qw2 = new QueryWrapper<>();
		qw2.ge("c.pro_time", startTime);
		qw2.lt("c.pro_time", endTime);
		if (labExpendWcIds != null && labExpendWcIds.size() > 0) {
			qw2.notIn("a.con_expend_no", labExpendWcIds);
		}
		List<ControlExpend> erpNolabList = controlExpendDao.findErpNolab(qw2);
		//	创建消耗数据
		Date date = new Date();
		List<LabExpend> labExpendList = new ArrayList<>();
		List<String> failBatch = new ArrayList<>();
		int successNum = 0, failNum = 0;
		for (int i = 0, s = erpNolabList.size(); i < s; i++) {
			ControlExpend controlExpend = erpNolabList.get(i);
			String conBatchNo = controlExpend.getConBatchNo();
			QueryWrapper<LabExpendBatch> qw3 = new QueryWrapper<>();
			qw3.eq("con_batch_id", conBatchNo);
			qw3.last("limit 1");
			LabExpendBatch labExpendBatch = labExpendBatchDao.selectOne(qw3);
			if (labExpendBatch == null) {
				failBatch.add(conBatchNo);
				failNum++;
				continue;
			}
			//	设置消耗数据
			Long batchId = labExpendBatch.getId();
			Long invoiceId = labExpendBatch.getInvoiceId();
			Long lineId = labExpendBatch.getLineId();
			Long erpMaterialId = controlExpend.getErpMaterialId();
			String storage = controlExpend.getStorage();
			String material = controlExpend.getMaterial();
			Double formulaQuantity = controlExpend.getFormulaQuantity();
			Double controlSetQuantity = controlExpend.getControlSetQuantity();
			Double controlRealQuantity = controlExpend.getControlRealQuantity();
			String conExpendNo = controlExpend.getConExpendNo();

			LabExpend labExpend = new LabExpend();
			labExpend.setInvoiceId(invoiceId);
			labExpend.setLineId(lineId);
			labExpend.setMaterialId(erpMaterialId);
			Long erpStorageId = controlExpend.getErpStorageId();
			labExpend.setStorageId(erpStorageId);
			labExpend.setControlStorage(storage);
			labExpend.setControlMaterial(material);
			labExpend.setBatchId(batchId);
			labExpend.setFormulaQuantity(formulaQuantity);
			labExpend.setControlSetQuantity(controlSetQuantity);
			labExpend.setControlRealQuantity(controlRealQuantity);
			Date useTime = controlExpend.getUseTime();
			labExpend.setUseTime(useTime);
			labExpend.setConExpendId(conExpendNo);
			labExpend.setStatus(StateEnum.ENABLED.getCode());
			labExpend.setCreator("111");
			labExpend.setCreateTime(date);
			labExpendList.add(labExpend);
			//	存储数据
			labExpendDao.insert(labExpend);
			successNum++;
		}
		System.err.println("成功数据:" + successNum + ";失败数据:" + failNum);
		String s = JSONArray.toJSONString(failBatch);
		log.error(s);
		//	释放琐
		redisUtils.releaseLock(redisLockKey);
	}

	/**
	 * 匹配没有工控机盘号的盘数据
	 *
	 * @param startTime
	 * @param endTime
	 */
	public void syncNoBatchNO(String startTime, String endTime) {
		String redisLockKey = "REDIS_BCS_SYNC_ERP_NO_CONTROL_BATCH_NO";
		boolean acquire = redisUtils.acquireLock(redisLockKey);
		if (!acquire) {
			//	没有加锁成功,不执行
			return;
		}
		//	查询erp盘中没有盘号的数据
		QueryWrapper<LabExpendBatch> qwbatch = new QueryWrapper<>();
		qwbatch.isNull("con_batch_id");
		List<LabExpendBatch> batchList = labExpendBatchDao.selectList(qwbatch);
		for (int i = 0, s = batchList.size(); i < s; i++) {
			LabExpendBatch labExpendBatch = batchList.get(i);
			//	取出发货单号
			String controlNo = labExpendBatch.getControlNo();
			Date batchTime = labExpendBatch.getBatchTime();
			//	查询工控机的盘数据
			QueryWrapper<ControlInvoice> qwi = new QueryWrapper();
			qwi.eq("control_invoice_no", controlNo);
			ControlInvoice controlInvoice = erpControlInvoiceService.getOne(qwi);
			if (controlInvoice == null) {
				continue;
			}
			Long controlInvoiceId = controlInvoice.getId();
			QueryWrapper<ControlExpendBatch> qw = new QueryWrapper<>();
			qw.eq("erp_invoice_id", controlInvoiceId);
			qw.eq("batch_time", batchTime);
			qw.last("limit 1");
			ControlExpendBatch expendBatch = controlExpendBatchDao.selectOne(qw);
			if (expendBatch == null) {
				continue;
			}
			String conBatchNo = expendBatch.getConBatchNo();
			labExpendBatch.setConBatchId(conBatchNo);
			labExpendBatchDao.updateById(labExpendBatch);
		}
		//	释放琐
		redisUtils.releaseLock(redisLockKey);
	}

	/**
	 * 同步强制完成的工控
	 *
	 * @param lineId
	 * @param dispatchId
	 * @return
	 */
	public boolean syncForceInvWcData(Long lineId, Long dispatchId) {
		if (dispatchId == null) {
			return false;
		}
		//	查询erp派单数据
		ProductionIndentDispatch erpDis = erpDispatchService.getById(dispatchId);
		//	查询工控机的发货单
		ProductionIndentDispatch wcData = productService.queryByDispatchId(lineId, dispatchId);
		if (wcData == null) {
			//	没有查询到数据
			return false;
		}
		//	设置派单数据
		//	生成方量
		Double prodMete = wcData.getProStere();
		//	生成时间
		Date prodTimE = wcData.getProTime();
		//	工控机的发货单id
		String prodId = wcData.getControlId();
		Integer controlProStatus = wcData.getControlProStatus();
		erpDis.setProStere(prodMete);
		erpDis.setProTime(prodTimE);
		erpDis.setControlId(prodId);
		erpDis.setControlProStatus(controlProStatus);
		//	保存派单数据
		erpDispatchService.updateById(erpDis);
		return true;
	}
}
